package vm

import (
	"bufio"
	"fmt"
	"os"
)

type Command func(instr uint16)

const (
	OP_BR   = iota /* branch */
	OP_ADD         /* add  */
	OP_LD          /* load */
	OP_ST          /* store */
	OP_JSR         /* jump register */
	OP_AND         /* bitwise and */
	OP_LDR         /* load register */
	OP_STR         /* store register */
	OP_RTI         /* unused */
	OP_NOT         /* bitwise not */
	OP_LDI         /* load indirect */
	OP_STI         /* store indirect */
	OP_JMP         /* jump */
	OP_RES         /* reserved (unused) */
	OP_LEA         /* load effective address */
	OP_TRAP        /* execute trap */
)

// 指令码集合
const (
	OPSET_R0   uint16 = 0x4EEE
	OPSET_R1   uint16 = 0x12F2
	OPSET_R2   uint16 = 0x0022
	OPSET_BASE uint16 = 0x00C0
	OPSET_PC   uint16 = 0x4C0D
)

// 中断信号
const (
	TRAP_GETC  = 0x20 /* get character from keyboard, not echoed onto the terminal */
	TRAP_OUT   = 0x21 /* output a character */
	TRAP_PUTS  = 0x22 /* output a word string */
	TRAP_IN    = 0x23 /* get character from keyboard, echoed onto the terminal */
	TRAP_PUTSP = 0x24 /* output a byte string */
	TRAP_HALT  = 0x25 /* halt the program */
)

func ins(instr uint16) {
	var r0, r1, r2, imm5, immFlag uint16
	var pcPlusOff, basePlusOff uint16
	// 获取指令码
	op := instr >> 12
	opbit := uint16(1 << op)

	if OPSET_R0&opbit > 0 {
		r0 = (instr >> 9) & 0x7
	}
	if OPSET_R1&opbit > 0 {
		r1 = (instr >> 6) & 0x7
	}
	if OPSET_R2&opbit > 0 {
		immFlag = (instr >> 5) & 0x1
		if immFlag > 0 {
			imm5 = signExtend(instr&0x1F, 5)
		} else {
			r2 = instr & 0x7
		}
	}
	if OPSET_BASE&opbit > 0 {
		// Base + offset
		basePlusOff = reg[r1] + signExtend(instr&0x3F, 6)
	}
	if OPSET_PC&opbit > 0 {
		// Indirect address PC + offset
		pcPlusOff = reg[R_PC] + signExtend(instr&0x1FF, 9)
	}
	if op == OP_BR {
		// BR
		cond := (instr >> 9) & 0x7
		if cond&reg[R_COND] > 0 {
			reg[R_PC] = pcPlusOff
		}
	} else if op == OP_ADD {
		if immFlag > 0 {
			reg[r0] = reg[r1] + imm5
		} else {
			reg[r0] = reg[r1] + reg[r2]
		}
	} else if op == OP_AND {
		if immFlag > 0 {
			reg[r0] = reg[r1] & imm5
		} else {
			reg[r0] = reg[r1] & reg[r2]
		}
	} else if op == OP_NOT {
		reg[r0] = ^reg[r1]
	} else if op == OP_JMP {
		reg[R_PC] = reg[r1]
	} else if op == OP_JSR {
		longFlag := (instr >> 11) & 1
		reg[R_R7] = reg[R_PC]
		if longFlag > 0 {
			pcPlusOff = reg[R_PC] + signExtend(instr&0x7FF, 11)
			reg[R_PC] = pcPlusOff
		} else {
			reg[R_PC] = reg[r1]
		}
	} else if op == OP_LD {
		reg[r0] = MemoryRead(pcPlusOff)
	} else if op == OP_LDI {
		reg[r0] = MemoryRead(MemoryRead(pcPlusOff))
	} else if op == OP_LDR {
		reg[r0] = MemoryRead(basePlusOff)
	} else if op == OP_LEA {
		reg[r0] = pcPlusOff
	} else if op == OP_ST {
		MemoryWrite(pcPlusOff, reg[r0])
	} else if op == OP_STI {
		MemoryWrite(MemoryRead(pcPlusOff), reg[r0])
	} else if op == OP_STR {
		MemoryWrite(basePlusOff, reg[r0])
	} else if op == OP_RTI {
		updateFlags(r0)
	} else if op == OP_TRAP {
		reg[R_R7] = reg[R_PC]
		switch instr & 0xFF {
		case TRAP_GETC:
			char, _, _ := bufio.NewReader(os.Stdin).ReadRune()
			reg[R_R0] = uint16(char)
			updateFlags(R_R0)
			break
		case TRAP_OUT:
			fmt.Printf("%c", reg[R_R0])
			break
		case TRAP_PUTS:
			i := reg[R_R0]
			for memory[i] > 0 {
				fmt.Printf("%c", memory[i])
				i++
			}
			break
		case TRAP_IN:
			fmt.Printf("Enter a character:")
			char, _, _ := bufio.NewReader(os.Stdin).ReadRune()
			fmt.Print(char)
			reg[R_R0] = uint16(char)
			updateFlags(R_R0)
			break
		case TRAP_PUTSP:
			i := reg[R_R0]
			for memory[i] > 0 {
				fmt.Printf("%c", memory[i]&0xFF)
				ch := memory[i] >> 8
				if ch > 0 {
					fmt.Printf("%c", ch)
				}
				i++
			}
			break
		case TRAP_HALT:
			fmt.Print("HALT")
			running = false
			break
		}
	}

	if 0x4666&opbit > 0 {
		updateFlags(r0)
	}
}
